package controllers

import java.io.File
import java.nio.file.Files

import command.{CommandExec, ExecCommand}
import javax.inject.Inject
import dao.BgAnnoDao
import org.apache.commons.io.FileUtils
import play.api.mvc._
import play.api.data.Form
import play.api.data.Forms._
import play.api.libs.json.Json
import utils._

import scala.collection.JavaConverters._
import models.Tables._

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import implicits.Implicits._
import tool.Pojo.CommandData

class GenomeToolController @Inject()(cc: ControllerComponents, bgAnnoDao: BgAnnoDao) extends AbstractController(cc) {

  def blastGeneBefore = Action {
    Ok(views.html.genome.tool.blastGene())
  }

  def blastGenomeBefore = Action {
    Ok(views.html.genome.tool.blastGenome())
  }

  def seqQueryBefore = Action {
    Ok(views.html.genome.tool.seqQuery())
  }

  def seqMultipleQueryBefore = Action {
    Ok(views.html.genome.tool.seqMultipleQuery())
  }

  case class QueryData(method: String, queryText: String, db: String, evalue: String, wordSize: String, maxTargetSeqs: String)

  val regionForm = Form(
    mapping(
      "chr" -> text,
      "start" -> number,
      "end" -> number
    )(RegionData.apply)(RegionData.unapply)
  )

  case class RegionStrData(region: String)

  val regionStrForm = Form(
    mapping(
      "region" -> text
    )(RegionStrData.apply)(RegionStrData.unapply)
  )

  val queryForm = Form(
    mapping(
      "method" -> text,
      "queryText" -> text,
      "db" -> text,
      "evalue" -> text,
      "wordSize" -> text,
      "maxTargetSeqs" -> text
    )(QueryData.apply)(QueryData.unapply)
  )

  def seqQuery = Action { implicit request =>
    val data = regionForm.bindFromRequest.get
    val tmpDir = Files.createTempDirectory("tmpDir").toFile
    val outFile = new File(tmpDir, "data.txt")
    val queryStr = data.chr + ":" + data.start + "-" + data.end
    val execCommand = new ExecCommand
    val command = Utils.samtoolsPath + " faidx " + Utils.HPbDataV2File.getAbsolutePath + " " + queryStr
    execCommand.exec(command, outFile, tmpDir)
    if (execCommand.isSuccess) {
      val dataStr = FileUtils.readFileToString(outFile)
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("data" -> dataStr))
    } else {
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("valid" -> "false", "message" -> execCommand.getErrStr))
    }
  }

  def seqMultipleQuery = Action { implicit request =>
    val data = regionStrForm.bindFromRequest.get
    val tmpDir = Files.createTempDirectory("tmpDir").toFile
    val outFile = new File(tmpDir, "data.txt")
    val queryStr = data.region
    val execCommand = new ExecCommand
    val command = Utils.samtoolsPath + " faidx " + Utils.HPbDataV2File.getAbsolutePath + " " + queryStr
    execCommand.exec(command, outFile, tmpDir)
    if (execCommand.isSuccess) {
      val dataStr = FileUtils.readFileToString(outFile)
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("data" -> dataStr))
    } else {
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("valid" -> "false", "message" -> execCommand.getErrStr))
    }
  }

  def blastGene = Action(parse.multipartFormData) { implicit request =>
    val data = queryForm.bindFromRequest.get
    val tmpDir = Files.createTempDirectory("tmpDir").toFile
    val seqFile = new File(tmpDir, "seq.fasta")
    data.method match {
      case "text" =>
        FileUtils.writeStringToFile(seqFile, data.queryText)
      case "file" =>
        val file = request.body.file("file").get
        file.ref.moveTo(seqFile, replace = true)
    }
    val outXmlFile = new File(tmpDir, "data.xml")
    val outHtmlFile = new File(tmpDir, "data.html")
    val cdsFile = data.db match {
      case "myDb" => Utils.cdsDataFile
      case "otherDb" => Utils.othercdsFile
      case "myDb2" => new File(Utils.path, "HG.gene.cds2")
    }
    val command =
      s"""
         | ${Utils.blastFile.unixPath} -query ${seqFile.unixPath}  -db ${cdsFile.unixPath}  -outfmt 5 -evalue  ${data.evalue}  -max_target_seqs  ${data.maxTargetSeqs} -word_size ${data.wordSize}  -out  ${outXmlFile.unixPath}
         | python   ${Utils.blast2HtmlFile.unixPath}  -i  ${outXmlFile.unixPath} -o  ${outHtmlFile.unixPath}
         |""".stripMargin
    val finalCommand = if (Utils.isWindows) command.conda() else command
    val execCommand = CommandExec().exec { b =>
      CommandData(tmpDir, List(finalCommand))
    }
    if (execCommand.isSuccess) {
      val html = FileUtils.readFileToString(outHtmlFile)
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("html" -> (html + "\n" + Utils.scriptHtml)))
    } else {
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("valid" -> "false", "message" -> execCommand.errorInfo))
    }
  }

  def blastGenome = Action(parse.multipartFormData) { implicit request =>
    val data = queryForm.bindFromRequest.get
    val tmpDir = Files.createTempDirectory("tmpDir").toFile
    val seqFile = new File(tmpDir, "seq.fasta")
    data.method match {
      case "text" =>
        FileUtils.writeStringToFile(seqFile, data.queryText)
      case "file" =>
        val file = request.body.file("file").get
        file.ref.moveTo(seqFile, replace = true)
    }
    val outXmlFile = new File(tmpDir, "data.xml")
    val outHtmlFile = new File(tmpDir, "data.html")
    val faFile = data.db match {
      case "myDb" => Utils.HPbDataFile
      case "otherDb" => Utils.otherFaFile
      case "myDb2" => Utils.HPbDataV2File
    }

    val command =
      s"""
         | ${Utils.blastFile.unixPath} -query ${seqFile.unixPath}  -db ${faFile.unixPath}  -outfmt 5 -evalue  ${data.evalue}  -max_target_seqs  ${data.maxTargetSeqs} -word_size ${data.wordSize}  -out  ${outXmlFile.unixPath}
         | python   ${Utils.blast2HtmlFile.unixPath}  -i  ${outXmlFile.unixPath} -o  ${outHtmlFile.unixPath}
         |""".stripMargin
    val finalCommand = if (Utils.isWindows) command.conda() else command
    val execCommand = CommandExec().exec { b =>
      CommandData(tmpDir, List(finalCommand))
    }

    if (execCommand.isSuccess) {
      val html = FileUtils.readFileToString(outHtmlFile)
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("html" -> (html + "\n" + Utils.scriptHtml)))
    } else {
      Utils.deleteDirectory(tmpDir)
      Ok(Json.obj("valid" -> "false", "message" -> execCommand.errorInfo))
    }
  }

}
